
/**
 *
 */
package com.serviceprovider.business.module.admin.transaction.controller;

//~--- non-JDK imports --------------------------------------------------------

import com.serviceprovider.business.module.admin.role.services.dao.interfaces.IRoleService;
import com.serviceprovider.business.module.admin.settings.services.ISettingsService;
import com.serviceprovider.business.module.admin.transaction.model.NewCsvReplica;
import com.serviceprovider.business.module.admin.transaction.services.dao.interfaces.ITransactionDataService;
import com.serviceprovider.business.module.admin.transaction.services.dao.interfaces.ITransactionImportDataService;
import com.serviceprovider.business.module.admin.transaction.utility.StringUtils;
import com.serviceprovider.business.module.admin.transaction.valueobjects.CsvMapping;
import com.serviceprovider.business.module.admin.user.services.dao.interfaces.IUserService;
import com.serviceprovider.business.module.sp.transaction.model.TransactionStatus;
import com.serviceprovider.business.module.sp.transaction.services.dao.interfaces.ITransStatusDao;
import com.serviceprovider.business.utility.ApplicationConfigProperties;
import com.serviceprovider.business.utility.RequestConstants;
import com.serviceprovider.business.utility.maintenance.settings.RoleUtils;

import org.apache.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

//~--- JDK imports ------------------------------------------------------------

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

/**
 * @author mrduongnv@gmail.com
 *
 */
@Controller
public class TransactionTransformerController {

    /** The logger. */
    private final Logger                  logger = Logger.getLogger(getClass());
    @Autowired
    private ApplicationConfigProperties   configProperties;
    @Autowired
    private CsvMapping                    csvMapping;
    @Autowired
    private IRoleService                  roleService;
    @Autowired
    private ISettingsService              settingsDao;
    @Autowired
    private ITransStatusDao               transStatusDao;
    @Autowired
    private ITransactionDataService       transactionDataService;
    @Autowired
    private ITransactionImportDataService transactionImportDataService;
    @Autowired
    private IUserService                  userService;

    @ModelAttribute
    @RequestMapping(
        value  = RequestConstants.TransactionRequests.TRANSFORM_DATA,
        method = RequestMethod.GET
    )
    @Transactional
    public ModelAndView transform(HttpServletRequest request) {
        final ModelAndView modelAndView =
            new ModelAndView(RequestConstants.TransactionRequests.VIEW_CREATE_DATA_MAPPING);
        final String        historyId         = request.getParameter("historyId");
        Integer             roleId            = this.roleService.getRoleId(RoleUtils.ROLE_SERVICE_PROVIDER);
        List<NewCsvReplica> nonDuplicatedList = new ArrayList<NewCsvReplica>();

        modelAndView.addObject("error", Boolean.FALSE);

        if (StringUtils.isEmpty(historyId)) {
            return new ModelAndView(RequestConstants.TransactionRequests.TRANSACTION_SUMMARY_VIEW);
        }

        try {
            final List<NewCsvReplica> results =
                this.transactionImportDataService.retrieveImportedTransactionDataListByHistoryId(
                    Integer.valueOf(historyId));
            String uniqueField   = this.csvMapping.getTransformUniqueId();
            String uniqueDbField = this.csvMapping.getTransformUniqueDbField();

            for (NewCsvReplica newCsvReplica : results) {
                final String uniqueFieldValue = getValueOfField(newCsvReplica, uniqueField);

                if ((uniqueFieldValue != null)
                        &&!this.transactionDataService.isDuplicatedRecord(uniqueFieldValue, uniqueDbField)) {
                    nonDuplicatedList.add(newCsvReplica);
                }
            }

            List<String> nonMatchingImportIds = this.transactionDataService.getAllNonMatchingUserIds(nonDuplicatedList);

            if ((nonMatchingImportIds == null) || nonMatchingImportIds.isEmpty()) {
                modelAndView.addObject("message", "No records to transform.");
                modelAndView.addObject("error", Boolean.TRUE);

                return modelAndView;
            }

            List<Long> nonMatchingOriginalIds = this.userService.getAllNonMatchingIds(roleId);

            if ((nonMatchingOriginalIds == null) || nonMatchingOriginalIds.isEmpty()) {
                modelAndView.addObject(
                    "message", "No service provider to map. Please first create a user and then try to transform.");
                modelAndView.addObject("error", Boolean.TRUE);

                return modelAndView;
            }

            modelAndView.addObject("imported_ids", nonMatchingImportIds);
            modelAndView.addObject("original_ids", nonMatchingOriginalIds);
            modelAndView.addObject("transactionData", "");
            request.getSession().setAttribute("newCsvReplicas", nonDuplicatedList);
            request.getSession().setAttribute("historyId", historyId);
        } catch (Exception e) {
            modelAndView.addObject("error", Boolean.TRUE);
            this.logger.error("Error occured when records are loaded.", e);
            modelAndView.addObject("message", "Error occured when records are loaded.");

            return modelAndView;
        }

        modelAndView.addObject("message", "Transaction records are loaded successfully.");

        return modelAndView;
    }

    @SuppressWarnings("unchecked")
    @Transactional
    @RequestMapping(
        value  = RequestConstants.TransactionRequests.CREATE_MAPPING,
        method = RequestMethod.POST
    )
    public ModelAndView onCreateMapping(HttpServletRequest request) throws Exception {
        ModelAndView mov          = new ModelAndView(RequestConstants.TransactionRequests.TRANSACTION_SUMMARY_VIEW);
        String[]     imported_ids = null;
        String[]     original_ids = null;

        try {
            if ((request.getParameter("original_ids") != null) && (request.getParameter("imported_ids") != null)) {
                original_ids = request.getParameter("original_ids").split(",");
                imported_ids = request.getParameter("imported_ids").split(",");

                String[] filteredImportedIds = this.transactionImportDataService.saveIdMappings(original_ids,
                                                   imported_ids);

                mov = loadData((List<NewCsvReplica>) request.getSession().getAttribute("newCsvReplicas"), original_ids,
                               filteredImportedIds, mov);
            }
        } catch (Exception e) {
            request.setAttribute("error", Boolean.TRUE);
            request.setAttribute("message", "Error occured while saving mapping data." + e);
            e.printStackTrace();
        }

        return mov;
    }

    private ModelAndView loadData(List<NewCsvReplica> newCsvReplicas, String[] original_ids, String[] imported_ids,
                                  ModelAndView mov) {
        try {
            if ((newCsvReplicas == null) || newCsvReplicas.isEmpty()) {
                mov.addObject("message", "No records found to transform.");

                return mov;
            }

            List<String> importedList = Arrays.asList(imported_ids);
            List<String> sqlList      = new ArrayList<String>();
            List<String> updateList   = new ArrayList<String>();
            int          i            = 0;

            for (NewCsvReplica newCsvReplica : newCsvReplicas) {
                if (importedList.contains(newCsvReplica.getService_provider_id())) {
                    int     userId                 = Integer.valueOf(original_ids[i]).intValue();
                    Double  defaultServiceFee      = getDefaultServiceFee();
                    Integer newTransactionStatusId = configProperties.getNewImportTransactionStatus();    // getNewTransactionStatusId();

                    i++;

                    StringBuilder             builder        = new StringBuilder("INSERT INTO sp_target_transformation (");
                    StringBuilder             nameBuilder    = new StringBuilder();
                    StringBuilder             valueBuilder   = new StringBuilder();
                    final Map<String, String> dbTransformMap = this.csvMapping.getDbTransformMap();

                    if (!dbTransformMap.isEmpty()) {
                        for (Map.Entry<String, String> entry : dbTransformMap.entrySet()) {
                            final String property = entry.getValue();

                            if ((property != null) &&!property.trim().isEmpty()) {
                                String value = getValueOfField(newCsvReplica, property);

                                if ("identification_number".equals(property)) {
                                    final Map<String, String> map = StringUtils.extractOriginatorCodeAndSSN(value);

                                    buildSql(nameBuilder, valueBuilder, "customer_ssn", map.get("ssn"), true);
                                    buildSql(nameBuilder, valueBuilder, "originator_name", map.get("originatorCode"),
                                             true);
                                } else {
                                    buildSql(nameBuilder, valueBuilder, entry.getKey(), value, false);
                                }
                            }
                        }

                        if (!nameBuilder.toString().contains("user_id")) {
                            buildSql(nameBuilder, valueBuilder, "user_id", Integer.valueOf(userId), false);
                        }

                        if (!nameBuilder.toString().contains("service_fee") && (defaultServiceFee != null)) {
                            buildSql(nameBuilder, valueBuilder, "service_fee", defaultServiceFee, false);
                        }

                        if (!nameBuilder.toString().contains("transaction_status_id")
                                && (newTransactionStatusId != null)) {
                            buildSql(nameBuilder, valueBuilder, "transaction_status_id", newTransactionStatusId, false);
                        }

                        builder.append(nameBuilder);
                        builder.append(") VALUES (");
                        builder.append(valueBuilder);
                        builder.append(")");
                        System.out.println(builder.toString());
                        sqlList.add(builder.toString());
                        updateList.add("update newcsvreplica set processed=1 where processed=0 and id="
                                       + newCsvReplica.getId());
                    }
                }
            }

            this.transactionDataService.loadTransactionRecord(sqlList, updateList);
            mov.addObject("message", "Transaction records are transformed successfully.");
        } catch (Exception e) {
            mov.addObject("error", Boolean.TRUE);
            this.logger.error("Error occured when records are loaded.", e);
            mov.addObject("message", "Error occured when records are loaded.");
        }

        return mov;
    }

    /**
     * Gets the new transaction status id.
     *
     * @return the new transaction status id
     */
    private Integer getNewTransactionStatusId() {
        final List<TransactionStatus> statusList = this.transStatusDao.findAll();

        for (TransactionStatus transStatus : statusList) {
            if ("New".equals(transStatus.getName()) && transStatus.isActive()) {
                return transStatus.getId();
            }
        }

        return null;
    }

    /**
     * Builds the sql.
     *
     * @param nameBuilder
     *            the name builder
     * @param valueBuilder
     *            the value builder
     * @param name
     *            the name
     * @param value
     *            the value
     * @param isString
     *            the is string
     */
    private void buildSql(StringBuilder nameBuilder, StringBuilder valueBuilder, String name, Object value,
                          boolean isString) {
        setNameAndValues(nameBuilder, valueBuilder, name, isString);
        valueBuilder.append(value);

        if (isString) {
            valueBuilder.append("'");
        }
    }

    /**
     * Gets the default service fee.
     *
     * @return the default service fee
     */
    private double getDefaultServiceFee() {
        return settingsDao.getDefaultFee() / 100.0;
    }

    /**
     * Gets the value of field.
     *
     * @param newCsvReplica
     *            the new csv replica
     * @param property
     *            the property
     * @return the value of field
     * @throws NoSuchMethodException
     *             the no such method exception
     * @throws IllegalAccessException
     *             the illegal access exception
     * @throws InvocationTargetException
     *             the invocation target exception
     */
    private String getValueOfField(NewCsvReplica newCsvReplica, final String property)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Class  c          = newCsvReplica.getClass();
        String methodName = "get".concat(StringUtils.makeFirstLetterCapital(property));
        Method mthd       = c.getMethod(methodName);
        String value      = (String) mthd.invoke(newCsvReplica);

        return value;
    }

    private void setNameAndValues(StringBuilder nameBuilder, StringBuilder valueBuilder, String value,
                                  boolean nextString) {
        if (nameBuilder.length() != 0) {
            nameBuilder.append(",");
        }

        if (valueBuilder.length() != 0) {
            valueBuilder.append(",");
        }

        nameBuilder.append(value);

        if (nextString) {
            valueBuilder.append("'");
        }
    }

    public ISettingsService getSettingsDao() {
        return settingsDao;
    }

    public void setSettingsDao(ISettingsService settingsDao) {
        this.settingsDao = settingsDao;
    }
}



